package playGround.dungeon;


import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import playGround.border.Border;
import playGround.border.Door;
import playGround.border.Wall;
import playGround.square.Scale;
import playGround.square.Square;

import exceptions.IllegalPositionException;
import exceptions.SlipperinessOverflowException;

public class DungeonTest {
	private Dungeon dungeon;
	private Position maxPosition;
	private Position position;
	private Square square;
	private Square square1;
	private Square square2;
	private Square square3;
	private Square square4;
	private Square square5;
	private Map<Direction, Border> borders;
	@Before
	public void setUp() throws Exception {
		maxPosition=new Position(100,100,100);
		dungeon= new Dungeon(maxPosition);
		position = new Position(1,2,1);
		Border wall1=new Wall(false);
		Border wall2 = new Wall(false);
		borders = new HashMap<Direction, Border>();
		borders.put(Direction.FLOOR,wall1);
		borders.put(Direction.EAST, wall2);
		square = new Square(300,new BigDecimal(10), Scale.CELCIUS, borders);
		Map<Direction,Border>borders6 = new HashMap<Direction, Border>();
		borders6.put(Direction.FLOOR, new Wall(false));
		borders6.put(Direction.FLOOR, new Wall(false));
		square1 = new Square(200,new BigDecimal(90), Scale.CELCIUS, borders6);
		//dungeon.addSquare(position, square);
		Border wall3=new Wall(false);
		Border wall4 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall3);
		borders1.put(Direction.EAST, wall4);
		square2 = new Square(100,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border wall = new Wall(false);
		borders5.put(Direction.FLOOR,wall);
		square3 = new Square(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall5 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall5);
		square4 = new Square(100,new BigDecimal(10), Scale.CELCIUS, borders2);
		Map<Direction,Border>borders3 = new HashMap<Direction, Border>();
		Border door1=new Door();
		borders3.put(Direction.NORTH,door1);
		square5= new Square(100,new BigDecimal(10), Scale.CELCIUS, borders3);
	}

	@After
	public void tearDown() throws Exception {
	}
	@Test
	public void testInvariant(){
		assert(dungeon.hasValidSquaresOnPosition());
	}
	@Test(expected = IllegalArgumentException.class)
	public void canHaveMaxDimTest(){
		Position position = new Position(-5,20,20);
		new Dungeon(position);
	}
	@Test(expected = IllegalArgumentException.class)
	public void addSquareNullTest(){
		dungeon.addSquare(position, null);
	}
	@Test(expected = IllegalArgumentException.class)
	public void addSquareAlreadyExistTest(){
		dungeon.addSquare(position, square);
		dungeon.addSquare(maxPosition, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPosition1(){
		Position position1 = new Position(1,1,1);
		dungeon.addSquare(position1, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPositionlargerMax(){
		Position position1 = new Position(200,1,1);
		dungeon.addSquare(position1, square);
	}
	@Test(expected = IllegalPositionException.class)
	public void addSquareIllegalPositionAlreadyExists(){
		Position position1 = new Position(1,1,1);
		dungeon.addSquare(position1, square);
		dungeon.addSquare(position1, square1);
	}
	@Test
	public void addSquareTest(){
		Position position1 = new Position(2,1,1);
		dungeon.addSquare(position1, square);
		assertTrue(dungeon.dungeonHasSquare(square));
	}
	@Test
	public void removeSquareTestOnPos(){
		Position position = new Position(1,2,3);
		dungeon.addSquare(position, square);
		dungeon.removeSquare(position);
		assertFalse(dungeon.positionHasSquare(position));
	}
	@Test
	public void removeSquareTestOnSquare(){
		Position position = new Position(1,2,3);
		dungeon.addSquare(position, square);
		dungeon.removeSquare(square);
		assertFalse(dungeon.dungeonHasSquare(square));
	}
	@Test(expected = SlipperinessOverflowException.class)
	public void addSquareSlipperyFail(){
		Border wall1=new Wall(true);
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall1);
		borders1.put(Direction.EAST, wall2);
		Square square2 = new Square(300,new BigDecimal(10), Scale.CELCIUS, borders1);
		dungeon.addSquare(new Position(1,2,3), square2);
	}
	@Test
	public void addSquareNextToEachOtherSameArea(){
		Position position = new Position(1,1,2);
		Position otherPosition = new Position(1,2,2);
		dungeon.addSquare(position, square);
		dungeon.addSquare(otherPosition, square1);
		assertEquals(250,square.getTemperature());
		assertEquals(250,square1.getTemperature());
		assertEquals(new BigDecimal(50.00).setScale(2),square.getHumidity());
		assertEquals(new BigDecimal(50.00).setScale(2),square1.getHumidity());
	}
	@Test
	public void addSquareNextToEachOtherWithBorder(){
		Border wall1=new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.SOUTH,wall1);
		Position position = new Position(1,3,1);
		Position otherPosition = new Position(1,2,1);
		Square square2 = new Square(200,new BigDecimal(10), Scale.CELCIUS, borders1);
		dungeon.addSquare(position, square);
		dungeon.addSquare(otherPosition, square2);
		assertEquals(300,square.getTemperature());
		assertEquals(200,square2.getTemperature());
		assertEquals(new BigDecimal(10.00).setScale(2),square.getHumidity());
		assertEquals(new BigDecimal(10.00).setScale(2),square2.getHumidity());
	}
	@Test
	public void addSquareSlipperyWork(){
		Border wall1=new Wall(false);
		Border wall2 = new Wall(false);
		Map<Direction,Border>borders1 = new HashMap<Direction, Border>();
		borders1.put(Direction.FLOOR,wall1);
		borders1.put(Direction.EAST, wall2);
		Square square2 = new Square(200,new BigDecimal(10), Scale.CELCIUS, borders1);
		Map<Direction,Border>borders5 = new HashMap<Direction, Border>();
		Border door1 = new Door();
		borders5.put(Direction.WEST,door1);
		Square square3 = new Square(100,new BigDecimal(10), Scale.CELCIUS, borders5);
		Border wall3 = new Wall(false);
		Map<Direction,Border>borders2 = new HashMap<Direction, Border>();
		borders2.put(Direction.CEILING,wall3);
		Square square4 = new Square(50,new BigDecimal(30), Scale.CELCIUS, borders2);
		Map<Direction,Border>borders4 = new HashMap<Direction, Border>();
		Border door = new Door();
		borders4.put(Direction.NORTH,door);
		Square square5 = new Square(300,new BigDecimal(10), Scale.CELCIUS, borders4);
		Map<Direction,Border>borders3 = new HashMap<Direction, Border>();
		Border wall4 = new Wall(true);
		borders3.put(Direction.WEST, wall4);
		Square square6 = new Square(300,new BigDecimal(10), Scale.CELCIUS, borders3);
		dungeon.addSquare(new Position(1,2,3), square2);
		dungeon.addSquare(new Position(1,2,2), square3);
		dungeon.addSquare(new Position(1,3,4), square4);
		dungeon.addSquare(new Position(1,1,2), square5);
		dungeon.addSquare(new Position(1,4,4), square6);
	}
	@Test
	public void testSamePosition(){
		dungeon.addSquare(position, square);
		Position position1 = new Position(1,2,1);
		assertTrue(position1.equals(position));
		Position position2 = new Position(2,1,1);
		assertEquals(position.hashCode(),position1.hashCode());
		assertNotSame(position1.hashCode(),position2.hashCode());
		assertEquals(true,dungeon.positionHasSquare(position1));
	}
	@Test
	public void calculateAreaTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		Set<Position> positionSet=new HashSet<Position>();
		Set<Position> allreadyDone=new HashSet<Position>();
		positionSet.add(position);
		positionSet.add(position1);
		positionSet.add(position2);
		assertEquals(dungeon.calculateArea(position2, allreadyDone),positionSet);
	}
	@Test
	public void areaTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertTrue(square4.isInSameArea(square2));
	}
	@Test
	public void temperatureChangeAfterAreaChange(){
		square4.setTemperature(400);
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(square2.getTemperature(),200);
		assertEquals(square4.getTemperature(),200);

	}
	@Test
	public void humidityChangeAfterAreaChange(){
		square4.setHumidity(new BigDecimal(40));
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(square2.getHumidity(),new BigDecimal(20).setScale(2));
		assertEquals(square4.getHumidity(),new BigDecimal(20).setScale(2));
	}
	@Test
	public void areaTestAfterRemoval(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.removeSquare(position1);
		assertFalse(square2.isInSameArea(square4));
	}
	@Test
	public void areaTestAfterAdd(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position2, square4);
		dungeon.addSquare(position1, square3);
		assertTrue(square2.isInSameArea(square4));
	}
	@Test
	public void positionEqualsTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertTrue(dungeon.positionHasSquare(new Position(1,2,1)));
	}
	@Test
	public void areaIsInSameAreaDoorOpenTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		assertTrue(square5.getNbSquaresOfArea()==1);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertTrue(square5.isInSameArea(square3));
		assertTrue(square5.getNbSquaresOfArea()==3);
	}
	@Test
	public void temperatureInbalanceAfterDoorChange(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		square5.setTemperature(400);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(200,square3.getTemperature());
		assertEquals(200,square5.getTemperature());
	}
	@Test
	public void humidityInbalanceAfterDoorChange(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		square5.setHumidity(new BigDecimal(40));
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(square3.getHumidity(),new BigDecimal(20).setScale(2));
		assertEquals(square5.getHumidity(),new BigDecimal(20).setScale(2));
	}
	@Test
	public void areaIsInSameAreaDoorCloseTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		square5.getBorder(Direction.NORTH).openOrClose();
		assertFalse(square5.isInSameArea(square3));
		
	}
	@Test
	public void borderConflictTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Border testWall= new Wall(false);
		square2.addBorder(Direction.NORTH, testWall );
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		assertEquals(square2.getBorder(Direction.SOUTH), square3.getBorder(Direction.NORTH));
	}
	@Test
	public void borderAddTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position1, position, new Wall(false));
		assertFalse(square2.isInSameArea(square4));
	}
	@Test
	public void borderRemovalTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position, position1, new Wall(false));
		dungeon.removeBorderBetweenTwoSquares(position1, position);
		assertTrue(square2.isInSameArea(square4));
	}
	@Test(expected= IllegalArgumentException.class)
	public void borderRemovalExceptionTest(){
		Position position1=new Position(1,2,1);
		Position position2=new Position(1,3,1);
		dungeon.addSquare(position1, square2);
		dungeon.removeBorderBetweenTwoSquares(position1, position2);
	}
	@Test(expected = IllegalArgumentException.class)
	public void borderRemovalExceptionTest2(){
		Position position1=new Position(1,2,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position2, square2);
		dungeon.addSquare(position1, square3);
		dungeon.removeBorderBetweenTwoSquares(position1, position2);
	}
	@Test
	public void temperatureInbalanceAfterBorderRemovalTest(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		dungeon.addBorderBetweenTwoSquares(position, position1, new Wall(false));
		square3.setTemperature(400);
		dungeon.removeBorderBetweenTwoSquares(position, position1);
		assertEquals(200, square2.getTemperature());
		assertEquals(200, square4.getTemperature());
	}
	@Test
	public void minMaxTemperatureAfterDoorChange(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		square5.setMaxTemperature(10000);
		dungeon.addSquare(position, square3);
		dungeon.addSquare(position1, square4);
		dungeon.addSquare(position2, square5);
		square5.getBorder(Direction.NORTH).openOrClose();
		assertEquals(square3.getMaxTemperature(),10000);
	}
	@Test
	public void minMaxTemperatureAfterAddingSquare(){
		Position position=new Position(1,2,1);
		Position position1=new Position(1,3,1);
		Position position2=new Position(1,4,1);
		square4.setMaxTemperature(10000);
		dungeon.addSquare(position, square2);
		dungeon.addSquare(position1, square3);
		dungeon.addSquare(position2, square4);
		assertEquals(10000,square2.getMaxTemperature());
	}
	

}
